package com.tjp.redis;

import com.tjp.exception.ShareLockException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @program: ape-frame
 * @description:
 * @author: aaronTang
 * @create: 2024-05-21
 */
@Component
public class RedisShareLockUtil {

    private Long TIME_OUT = 100000L;

    @Resource
    private RedisUtil redisUtil;


    /**
     * 加锁
     * @param lockKey 锁的key
     * @param requestId 区分锁
     * @param time 加锁时间
     * @return
     */
    public boolean lock(String lockKey, String requestId, Long time) {
        //requestId用于区分是否为同一把锁

        //1.参数校验
        if (StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
            throw new ShareLockException("加锁参数不合法");
        }

        //1.2设置超时时间 固定时间内没有获取到锁就退出 当前毫秒级时间+固定时间
        long currentTime = System.currentTimeMillis();
        long outTime = currentTime + TIME_OUT;

        //1.3设置加锁标记
        Boolean result = false;
        //2.加锁自旋,看是否可以等待一会儿加锁成功
        while (currentTime < outTime) {
            //3.借助redis的setnx来进行锁的设置  MILLISECONDS毫秒级  setnx:set if not exist当key不存在时,设置key的值为某个字符串并返回成功
            result = redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);
            //3.1为什么要用setNx  保证原子性
            if (result) {
                //加锁成功
                return result;
            }
            //加锁失败 休眠10s后再去争抢锁
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //更新当前时间
            currentTime = System.currentTimeMillis();
        }

        return result;
    }

    /**
     * 解锁
     * @return
     */
    public boolean unLock(String lockKey, String requestId) {
        if (StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId)) {
            throw new ShareLockException("解锁参数不合法");
        }


        try {
            String value = redisUtil.get(lockKey);
            //需要通过requestId和获取的value判断是否为同一把锁
            if (requestId.equals(value)) {
                //只有同一把锁才可以解锁
                redisUtil.del(lockKey);
                return true;
            }
        }catch (Exception e) {
            //添加日志
        }
        return false;
    }


    /**
     * 尝试锁
     * @return
     */
    public boolean tryLock(String lockKey, String requestId, Long time) {
        //1.参数校验
        if (StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
            throw new ShareLockException("尝试加锁参数不合法");
        }

        return redisUtil.setNx(lockKey, requestId, time, TimeUnit.MICROSECONDS);
    }
}
